package org.ajida.linux.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.ajida.common.LogUtil;
import org.ajida.common.StringUtil;
import org.ajida.linux.util.struct.CpuTemperature;
import org.ajida.linux.util.struct.HdInfo;
import org.ajida.linux.util.struct.HdMountPointInfo;
import org.ajida.linux.util.struct.PortInfo;
import org.ajida.linux.util.struct.TopInfo;
import org.ajida.linux.util.struct.TopInfoVO;
import org.ajida.linux.util.struct.TopProcess;

import ch.ethz.ssh2.Connection;

/**
 * linux相关工具
 * @author CaiDdongyu
 * @date 2022-3-4
 */
public final class LinuxUtil {

	public static final String ENDLINE = "\r\n";
	
	public static void main(String[] args) {
	}
	
	private LinuxUtil() {}
	
	public static List<PortInfo> getPortInfo(Connection connection) throws Exception {
		List<PortInfo> list = new ArrayList<>();
		try {
			Map<String,PortInfo> map = new HashMap<>();
			String result = SSHUtil.exec(connection, "netstat -anp|grep :::", 3, true);
			if(result.contains("-bash")) {//说明没找到命令
				return list;
			}
			String[] lines = result.split(ENDLINE);
			for(String line:lines) {
				if(line.contains("::1:")) {
					continue;
				}
				line = line.trim();
				if(StringUtil.isEmpty(line)) {
					continue;
				}
				while(line.contains("  ")) {
					line = line.replaceAll("  ", " ");
				}
				String[] split = line.split(" ");
				String port = split[3].replaceAll(":::", "");
				PortInfo pi = new PortInfo();
				pi.setPort(port);
				pi.setUsed(1);
				
				map.put(port, pi);
			}
			
			
			
			return list;
		} catch (Exception e) {
			do {
				if(e.getMessage().contains("command not found")) {
					break;
				}
				if(e.getMessage().contains("No sensors found")) {
					break;
				}
				
				LogUtil.error(e);
			}while(false);
			
			return list;
		}
	}
	
	
	/**
	 * 获取cpu温度
	 * @return
	 */
	public static List<CpuTemperature> getCpuTemperature(Connection connection) throws Exception {
		List<CpuTemperature> list = new ArrayList<>();
		try {
			String result = SSHUtil.exec(connection, "sensors", 3, true);
			if(result.contains("-bash")) {//说明没找到命令
				return list;
			}
			String[] lines = result.split(ENDLINE);
			for(String line:lines) {
				if(line.startsWith("Package id") || line.startsWith("Physical id")) {
					String corePackage = line.substring(0,line.indexOf(":"));
					line = line.substring(line.indexOf(":")+1).trim();
					double temperature = Double.parseDouble(line.split("°C")[0]);
					CpuTemperature ct = new CpuTemperature();
					ct.setCorePackage(corePackage);
					ct.setTemperature(temperature);
					list.add(ct);
				}
			}
			return list;
		} catch (Exception e) {
			do {
				if(e.getMessage().contains("command not found")) {
					break;
				}
				if(e.getMessage().contains("No sensors found")) {
					break;
				}
				
				LogUtil.error(e);
			}while(false);
			
			return list;
		}
	}
	
	/**
	 * 获取磁盘使用情况
	 * @return
	 */
	public static HdInfo getHdInfo(Connection connection) throws Exception {
		HdInfo info = new HdInfo();
		String result = SSHUtil.exec(connection, "df", 3, true);
		String[] lines = result.split(ENDLINE);
		List<HdMountPointInfo> mountPointInfoList = new ArrayList<>();
		info.setMountPointInfoList(mountPointInfoList);
		
		long totalSize = 0;
		long freeSize = 0;
		for(String line:lines) {
			line = line.trim();
			if(line.startsWith("/dev/mapper/centos-")) {
				//root目录
				HdMountPointInfo mountPointInfo = getMountPointInfo(line);
				mountPointInfoList.add(mountPointInfo);
				totalSize = totalSize+mountPointInfo.getTotalSize();
				freeSize = freeSize+mountPointInfo.getFreeSize();
			}
		}
		
		info.setTotalSize(totalSize);
		info.setFreeSize(freeSize);
		info.setUsedSize(totalSize-freeSize);
		info.setUsedRate((int)(info.getUsedSize()*100/totalSize));
		return info;
	}
	
	public static HdMountPointInfo getMountPointInfo(String line) {
		while(true) {
			line = line.replaceAll("  ", " ").trim();
			if(!line.contains("  ")) {
				break;
			}
		}
		String[] split = line.split(" ");

		HdMountPointInfo info = new HdMountPointInfo();
		info.setFileSystem(split[0]);
		info.setTotalSize(Long.parseLong(split[1]));
		info.setUsedSize(Long.parseLong(split[2]));
		info.setFreeSize(Long.parseLong(split[3]));
		info.setUsedRate(Integer.parseInt(split[4].substring(0,split[4].length()-1)));
		info.setMountPoint(split[5]);
		
		return info;
	}
	
	/**
	 * 获取时区
	 * @return
	 */
	public static String getTimeZone(Connection connection) throws Exception {
		String result = SSHUtil.exec(connection, "timedatectl | grep \"Time zone\"", 3, true);
		return result.split(ENDLINE)[0].trim();
	}
	
	/**
	 * 获取系统时间
	 * @param connection
	 * @return
	 * @throws Exception
	 */
	public static String getDate(Connection connection) throws Exception {
		String result = SSHUtil.exec(connection, "date \"+%Y-%m-%d %H:%M:%S\"", 3, true);
		return result.split(ENDLINE)[0].trim();
	}
	
	/**
	 * 获取Top执行情况
	 * @param connection
	 * @return
	 * @throws Exception
	 */
	public static int getCpus(Connection connection) throws Exception {
		String[] split = SSHUtil.exec(connection, "lscpu |grep \"CPU(s):\"", 3, true).split(ENDLINE);
		int cpus = 0;
		for(String line:split) {
			line = line.trim();
			if(line.startsWith("CPU(s):")) {
				cpus = Integer.parseInt(line.substring(line.indexOf(":")+1).trim());
				break;
			}
		}
		return cpus;
	}
	
	public static TopInfoVO getTopInfo(Connection connection) throws Exception {
		String topContent = SSHUtil.exec(connection, "top -b -n 1", 3, true);
		String[] split = topContent.split(ENDLINE);
		TopInfoVO topInfo = new TopInfoVO();
		int content = 1;
		for(String line:split) {
			line = line.trim().toLowerCase();
			if(line.startsWith("pid ")) {
				content = 2;
				continue;
			}
			if(content == 1) {
				convertTopLine(line, topInfo);
				convertTaskLine(line, topInfo);
				convertCpuLine(line, topInfo);
				convertMemLine(line, topInfo);
			}
			if(content == 2) {
				convertProcessLine(line, topInfo);
			}
		}
		
		//计算下内存使用率
		if(topInfo.getTotalMemory() > 0) {
			topInfo.setMemoryUsedRate((topInfo.getTotalMemory()-topInfo.getAvailableMemory())*100/topInfo.getTotalMemory());
		}
		
		return topInfo;
	}
	
	public static void convertProcessLine(String lineToLowerCase,TopInfoVO topInfo) {
		try {
			while(true) {
				lineToLowerCase = lineToLowerCase.replaceAll("  ", " ").trim();
				if(!lineToLowerCase.contains("  ")) {
					break;
				}
			}
			String[] split = lineToLowerCase.split(" ");
			if(split.length < 12) {
				return;
			}
			String pid = split[0];
			double cpuRate = Double.parseDouble(split[8]);
			double memoryRate = Double.parseDouble(split[9]);
			String command = split[11];
			TopProcess process = new TopProcess();
			process.setPid(pid);
			process.setCpuRate(cpuRate);
			process.setMemoryRate(memoryRate);
			process.setCommand(command);
			
			//Top Cpu
			List<TopProcess> topCpuProcessList = topInfo.getTopCpuProcessList();
			if(topCpuProcessList == null) {
				topCpuProcessList = new LinkedList<>();
				topCpuProcessList.add(process);
				topInfo.setTopCpuProcessList(topCpuProcessList);
			}else {
				int i = 0;
				for(;i<topCpuProcessList.size();i++) {
					if(cpuRate>topCpuProcessList.get(i).getCpuRate()) {
						break;
					}
				}
				topCpuProcessList.add(i, process);
			}
			
			//Top memory
			List<TopProcess> topMemoryProcessList = topInfo.getTopMemoryProcessList();
			if(topMemoryProcessList == null) {
				topMemoryProcessList = new LinkedList<>();
				topMemoryProcessList.add(process);
				topInfo.setTopMemoryProcessList(topMemoryProcessList);
			}else {
				int i = 0;
				for(;i<topMemoryProcessList.size();i++) {
					if(memoryRate>topMemoryProcessList.get(i).getMemoryRate()) {
						break;
					}
				}
				topMemoryProcessList.add(i, process);
			}
		} catch (Exception e) {
			LogUtil.error(lineToLowerCase);
			throw e;
		}
	}
	
	public static void convertMemLine(String lineToLowerCase,TopInfo topInfo) {
		try {
			if(!lineToLowerCase.contains("mem")) {
				return;
			}
			if(lineToLowerCase.contains("swap")) {
				//分析可用内存
				long availableMemory = Long.parseLong(
														lineToLowerCase.substring(
																lineToLowerCase.lastIndexOf(".")+1,
																lineToLowerCase.indexOf("avail")
																).trim()
														);
				topInfo.setAvailableMemory(availableMemory);
				return;
			}
			
			//总内存
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(":")+1);
			long totalMemory = Long.parseLong(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setTotalMemory(totalMemory);
			
			//空闲内存总量，这个free的意思不是可用，可用内存要看avaliable mem
			long freeMemory = Long.parseLong(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setFreeMemory(freeMemory);
			
			//使用掉的内存总量
			long usedMemory = Long.parseLong(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setUsedMemory(usedMemory);
			
			//用作缓存交换的内存总量
			long buffMemory = Long.parseLong(lineToLowerCase.trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setBuffMemory(buffMemory);
		} catch (Exception e) {
			LogUtil.error(lineToLowerCase);
			throw e;
		}
	}
	
	public static void convertCpuLine(String lineToLowerCase,TopInfo topInfo) {
		try {
			if(!lineToLowerCase.contains("cpu")) {
				return;
			}
			
			//用户空间占用CPU百分比
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(":")+1);
			double cpuUsRate = Double.parseDouble(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setCpuUsRate(cpuUsRate);
			
			//内核空间占用CPU百分比
			double cpuSysRate = Double.parseDouble(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setCpuSysRate(cpuSysRate);
			
			//改变过优先级的进程占用CPU的百分比
			double cpuNiRate = Double.parseDouble(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setCpuNiRate(cpuNiRate);
			
			//空闲CPU百分比
			double cpuIdRate = Double.parseDouble(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setCpuIdRate(cpuIdRate);
		} catch (Exception e) {
			LogUtil.error(lineToLowerCase);
			throw e;
		}
	}
	
	public static void convertTaskLine(String lineToLowerCase,TopInfo topInfo) {
		try {
			if(!lineToLowerCase.startsWith("tasks")) {
				return;
			}
			//总进程数
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(":")+1);
			int totalTasks = Integer.parseInt(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setTotalTasks(totalTasks);
			
			//正在运行的进程数
			int runningTaks = Integer.parseInt(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setRunningTaks(runningTaks);
			
			//睡眠的进程数
			int sleepingTasks = Integer.parseInt(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setSleepingTasks(sleepingTasks);
			
			//停止的进程数
			int stoppedTasks = Integer.parseInt(lineToLowerCase.substring(0,lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setStoppedTasks(stoppedTasks);
		} catch (Exception e) {
			LogUtil.error(lineToLowerCase);
			throw e;
		}
	}
	
	public static void convertTopLine(String lineToLowerCase,TopInfo topInfo) {
		try {
			if(!lineToLowerCase.startsWith("top")) {
				return;
			}
			lineToLowerCase = lineToLowerCase.replaceAll("-", "");
			lineToLowerCase = lineToLowerCase.substring("top".length());
			String[] upSplit = lineToLowerCase.split(" up ");
			
			//系统当前时间
			String systemTime = upSplit[0].trim();
			topInfo.setSystemTime(systemTime);
			lineToLowerCase = upSplit[1].trim();
			
			//系统运行时长
			//要看uses在哪里，运行时长信息可能不止一个逗号
			String runningTime = lineToLowerCase.substring(0,lineToLowerCase.indexOf("user"));
			int lastIndexOfDot = runningTime.lastIndexOf(",");
			runningTime = runningTime.substring(0,lastIndexOfDot);
			lineToLowerCase = lineToLowerCase.substring(lastIndexOfDot+1);
			topInfo.setRunningTime(runningTime);
			
			//在线用户
			int onlineUsers = Integer.parseInt(lineToLowerCase.substring(0, lineToLowerCase.indexOf(",")).trim().split(" ")[0].trim());
			lineToLowerCase = lineToLowerCase.substring(lineToLowerCase.indexOf(",")+1);
			topInfo.setOnlineUsers(onlineUsers);
			
			//平均负载
			lineToLowerCase = lineToLowerCase.split(":")[1];
			String[] loadAverageSplit = lineToLowerCase.split(",");
			double loadAverageOneMin = Double.parseDouble(loadAverageSplit[0].trim());
			double loadAverageFiveMin = Double.parseDouble(loadAverageSplit[1].trim());
			double loadAverageFifteenMin = Double.parseDouble(loadAverageSplit[2].trim());
			topInfo.setLoadAverageOneMin(loadAverageOneMin);
			topInfo.setLoadAverageFiveMin(loadAverageFiveMin);
			topInfo.setLoadAverageFifteenMin(loadAverageFifteenMin);
		} catch (Exception e) {
			LogUtil.error(lineToLowerCase);
			throw e;
		}
	}
}
